package ei

import (
	"math"
)

func addAdvMathEx(ip *Interpreter) {
	ip.AddRawFun("diff", func(ip *Interpreter, v []Expr) Token {
		if len(v) != 1 {
			panic("求导参数错误，需要1个参数（计算式）")
		}
		return FromFloat64(ip.differentiate(v[0]))
	})
	ip.AddRawFun("inte", func(ip *Interpreter, v []Expr) Token {
		if len(v) != 3 {
			panic("积分参数错误，需要3个参数")
		}
		argToken := ParseExprArgs(ip, v[1], v[2])
		var min, max float64
		ParseArgs(argToken, &min, &max)
		return FromFloat64(ip.integration(v[0], min, max))
	})
	ip.AddFunc("interpolate", func(t Token) Token {
		var x float64
		var data []Token
		ParseArgs(t, &x, &data)
		xs := make([]float64, 0, len(data))
		ys := make([]float64, 0, len(data))
		for i := 0; i < len(data); i++ {
			var x0, y0 float64 = 0, 0
			ParseArgs(data[i], &x0, &y0)
			xs = append(xs, x0)
			ys = append(ys, y0)
		}
		return FromFloat64(interpolate(x, xs, ys))
	})
}

// 自变量用?表示，?后接变量名，该变量的值为求导位置
// 通过targetChange在计算时动态变化值，避免影响原变量
func (self *Interpreter) differentiate(expr Expr) float64 {
	self.targetChangeAdd = 0
	y1 := expr.Cal(self, true).ToFloat64()
	self.targetChangeAdd = self.AdvPrec
	y2 := expr.Cal(self, true).ToFloat64()
	// fmt.Printf("x1:%f; x2:%f; y1:%f; y2:%f\n", x, x2, y1, y2)
	return (y2 - y1) / self.AdvPrec
}

// 数值积分， min, max 为上限下线
func (self *Interpreter) integration(expr Expr, min, max float64) float64 {
	n := (max - min) / self.AdvPrec
	self.targetChangeAdd = min
	self.targetChangeMul = 0
	var valueNow = expr.Cal(self, true).ToFloat64()
	var newValue float64
	var ig float64 = 0
	for i := 0.0; i < n; i++ {
		self.targetChangeAdd = min + i*self.AdvPrec
		newValue = expr.Cal(self, true).ToFloat64()
		ig += (valueNow + newValue) * self.AdvPrec / 2
		valueNow = newValue
	}
	return ig
}

func interpolate(x float64, xs, ys []float64) float64 {
	if len(xs) != len(ys) {
		panic("给定x和y的个数不同")
	}
	n := len(xs)
	var l, L float64
	for i := 0; i < n; i++ {
		l = 1
		for j := 0; j < n; j++ {
			if i != j {
				l *= (x - xs[j]) / (xs[i] - xs[j])
			}
		}
		L += ys[i] * l
	}
	return L
}

func solve(fn Expr, ip Interpreter, startX float64) Token {
	// println("解方程")
	vars := fn.FindInVarNames()
	if len(vars)>1 {
		panic("暂不支持超过1个未知数的求解")
	} else if len(vars) == 0 {
		panic("没有找到需要求解的变量")
	}
	ip.Mem.SetValue(vars[0], FromFloat64(startX))
	// println(fn.Cal(&ip, true).ToFloat64())
	for math.Abs(fn.Cal(&ip, true).ToFloat64()) > ip.AdvPrec {
		ip.Mem.SetValue(vars[0], FromFloat64(ip.Mem.Get(vars[0]).ToFloat64() - fn.Cal(&ip, true).ToFloat64()/ip.differentiate(fn)))
	}
	println("solved "+ vars[0] + " =", ip.Mem.Get(vars[0]).ValueStr(), "Loss", fn.Cal(&ip, true).ToFloat64())
	r := ip.Mem.Get(vars[0])
	return r
}
